/**
 * Copyright (c) 2012, Brent Mucci
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/



#ifndef _MCL_BITSTREAM_BUFFER
#define _MCL_BITSTREAM_BUFFER

#include <fstream>
#include "mcl_bitstream.hpp"

namespace mcl 
{

/*
This class is a two buffered bitstream reader, capable of returning 0 - 32 bits in a single read or peek. 
A single buffer is necessary to cut down on the I/O with the second buffer containing the next set of buffered data. 
The second buffer is necessary for the peek(int) method to function properly. 
When the end of the first buffer is being reached and the peek is called it will need to read into the second buffer, 
preventing I/O from being read outside of a buffer load function. 
If the secondary buffer is empty then 0s are loaded in place of the additionally requested bits (effectively NULLs). 
Peek is a function of next_bits(int) as found in the H.264 specification.
*/

class BitstreamBuffer : Bitstream
{
public:
	BitstreamBuffer(std::istream &a_istream);
	virtual ~BitstreamBuffer();

	// *** See ITU-T June 2011 specification on the following methods:
	virtual bool byte_aligned();
	virtual bool more_data_in_byte_stream();
	virtual bool more_rbsp_data();
	virtual bool more_rbsp_trailing_data();
	virtual void rbsp_trailing_bits();
	virtual unsigned int next_bits(unsigned int n);
	virtual unsigned int read_bits(unsigned int n);
	// Read unsigned exponential golomb encoded bits
	virtual unsigned int read_bits_ue();
	// Read signed exponential golomb encoded bits
	virtual signed int read_bits_se();

protected:
	// Once data has been read in, step the buffer bit position forward.
	virtual void step(unsigned int bits);
	// Return an integer containing a number of bits that have been peeked.
	virtual unsigned int peek(unsigned int bits);
	// The number of bits necessary to reach the next byte alignment.
	virtual unsigned int bitsToAlign();

private:
	// 65536 bytes is the typical maximum size of a NAL but may change. 
	// Minimum is 32bit + 1 bit = 5 bytes!
	unsigned int static const BUFFER_SIZE = 32768; 
	// The stream that the H.264 data will be read from. 
	std::istream &m_istream;
	/*
	A stream buffer used to store bytes from the input stream. It must be unsigned for the compiler to use logical 
	shift operations, opposed to arithmetic shift.
	A primary and secondary buffer is necessary for a mandatory peek operation to take place successfully.
	If the primary buffer is peeked beyond its capacity the rest of the peek function will bleed into the secondary buffer.
	Once the primary buffer has been spent (read completely), the primary and secondary buffer pointers will swap and the 
	secondary buffer will load in new data.
	*/
	unsigned char  m_primaryStreamBuffer[BUFFER_SIZE];
	unsigned char  m_secondaryStreamBuffer[BUFFER_SIZE];
	// Pointers to the primary and secondary buffers. They will swap once the primary is spent.
	unsigned char* m_pPrimary;
	unsigned char* m_pSecondary;

	// The amount of the primary buffer that has been populated.
	// TODO: If the primary buffer is ever 0 then the video stream will return false for RBSP data, 
	// when streaming find another way to determine extra RBSP data. It depends on the mp_istream
	unsigned int m_primaryBufferSize; 
	// The amount of the secondary buffer that has been populated.
	unsigned int m_secondaryBufferSize;
	// The current offset, in bytes, of the stream buffer m_streamBuffer.
	size_t m_bufferBytePos;
	// The current offset, in bits, from the m_bufferPos.
	unsigned int m_bufferBitPos;
	// The size in bytes of the current input stream.
	size_t m_istreamSize;
	// Used to determine if the buffer needs to be switched
	bool m_isFlipped;
	// *** Internal methods ***
	// Gets the next set of buffered data from the input stream.
	bool readNextBuffer();
	// The current offset, in bytes, of the byte stream
	size_t m_bytePos;
	// The current offset, in bits, of the bitstream
	unsigned int m_bitPos;
};

};

#endif